package com.chenjim.glrecorder.surfacetexture;

import android.content.Context;
import android.graphics.SurfaceTexture;
import android.opengl.GLES11Ext;
import android.opengl.GLES30;
import android.opengl.GLSurfaceView;
import android.util.Log;

import org.apache.commons.io.IOUtils;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.charset.StandardCharsets;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

public class CameraRendererEx implements GLSurfaceView.Renderer, SurfaceTexture.OnFrameAvailableListener {
    private CameraGLSurfaceView mGLSurfaceView;
    private int mProgram;
    private int mProgram2D; // 屏幕渲染程序
    private int mOESTextureId;
    private int mFboId;
    private int mFboTextureId;
    private float[] mTransformMatrix = new float[16];
    private FloatBuffer mVertexBuffer;
    private FloatBuffer mTexCoordBuffer;
    private Context context;


    private int superResolutionUniform = 1;


    public int getmSuperResolutionUniform() {
        return superResolutionUniform;
    }

    public void setmSuperResolutionUniform(int mSuperResolutionUniform) {
        this.superResolutionUniform = mSuperResolutionUniform;
    }

    private static final float[] VERTICES = {
            -1.0f, -1.0f, 0.0f,
            1.0f, -1.0f, 0.0f,
            -1.0f, 1.0f, 0.0f,
            1.0f, 1.0f, 0.0f
    };

    private static final float[] TEX_COORDS = {
            0.0f, 0.0f,
            1.0f, 0.0f,
            0.0f, 1.0f,
            1.0f, 1.0f
    };

    public CameraRendererEx(Context context, CameraGLSurfaceView glSurfaceView) {
        this.context = context;
        mGLSurfaceView = glSurfaceView;

        // 初始化顶点缓冲区
        ByteBuffer bb = ByteBuffer.allocateDirect(VERTICES.length * 4);
        bb.order(ByteOrder.nativeOrder());
        mVertexBuffer = bb.asFloatBuffer();
        mVertexBuffer.put(VERTICES);
        mVertexBuffer.position(0);

        // 初始化纹理坐标缓冲区
        bb = ByteBuffer.allocateDirect(TEX_COORDS.length * 4);
        bb.order(ByteOrder.nativeOrder());
        mTexCoordBuffer = bb.asFloatBuffer();
        mTexCoordBuffer.put(TEX_COORDS);
        mTexCoordBuffer.position(0);
    }

    @Override
    public void onSurfaceCreated(GL10 unused, EGLConfig config) {
        initOES();
    }

    private void initOES() {
        GLES30.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

        // 生成外部纹理
        int[] textures = new int[1];
        GLES30.glGenTextures(1, textures, 0);
        mOESTextureId = textures[0];

        // 绑定外部纹理
        GLES30.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mOESTextureId);
        checkGlError("glBindTexture");

        // 设置纹理参数
        GLES30.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_NEAREST);
        GLES30.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_LINEAR);
        GLES30.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES30.GL_TEXTURE_WRAP_S, GLES30.GL_CLAMP_TO_EDGE);
        GLES30.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES30.GL_TEXTURE_WRAP_T, GLES30.GL_CLAMP_TO_EDGE);
        checkGlError("glTexParameter");

        // 创建 SurfaceTexture
        SurfaceTexture oldSurfaceTexture = mGLSurfaceView.getSurfaceTexture();
        if (oldSurfaceTexture != null) {
            oldSurfaceTexture.release();
        }

        SurfaceTexture surfaceTexture = new SurfaceTexture(mOESTextureId);
        surfaceTexture.setOnFrameAvailableListener(this);
        mGLSurfaceView.setSurfaceTexture(surfaceTexture);
        try {
            // 从 GLSL 文件加载着色器代码
            String vertexShaderCode =
                    IOUtils.toString(context.getAssets()
                            .open("surface_texture/vertex_shader.glsl"), StandardCharsets.UTF_8);
            String fragmentShaderCode = IOUtils.toString(context.getAssets()
                    .open("surface_texture/fragment_shader.glsl"), StandardCharsets.UTF_8);
            String vertexShaderScreenCode = IOUtils.toString(context.getAssets()
                    .open("surface_texture/vertex_shader_screen.glsl"), StandardCharsets.UTF_8);
            String fragmentShaderScreenCode = IOUtils.toString(context.getAssets().open("surface_texture/fragment_shader_screen.glsl"), StandardCharsets.UTF_8);

            // 创建主着色器程序
            mProgram = createProgram(vertexShaderCode, fragmentShaderCode);
            if (mProgram == 0) {
                throw new RuntimeException("Failed to create program.");
            }
            checkGlError("createProgram");

            // 创建屏幕渲染着色器程序
            mProgram2D = createProgram(vertexShaderScreenCode, fragmentShaderScreenCode);
            if (mProgram2D == 0) {
                throw new RuntimeException("Failed to create screen rendering program.");
            }
            checkGlError("createProgram2D");


        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 初始化帧缓冲对象（FBO）及其纹理附件
     *
     * @param width  渲染目标宽度
     * @param height 渲染目标高度
     */
    private void initFBO(int width, int height) {
        // 生成 FBO
        int[] fbos = new int[1];
        GLES30.glGenFramebuffers(1, fbos, 0);
        mFboId = fbos[0];
        checkGlError("glGenFramebuffers");

        // 生成用于 FBO 的纹理
        int[] textures = new int[1];
        GLES30.glGenTextures(1, textures, 0);
        mFboTextureId = textures[0];
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mFboTextureId);
        checkGlError("glBindTexture for FBO texture");

        // 设置纹理参数
        GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_LINEAR);
        GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_LINEAR);
        GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_S, GLES30.GL_CLAMP_TO_EDGE);
        GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_T, GLES30.GL_CLAMP_TO_EDGE);
        checkGlError("glTexParameteri for FBO texture");

        // 分配纹理存储（不上传任何数据）
        GLES30.glTexImage2D(GLES30.GL_TEXTURE_2D, 0, GLES30.GL_RGBA, width, height,
                0, GLES30.GL_RGBA, GLES30.GL_UNSIGNED_BYTE, null);
        checkGlError("glTexImage2D for FBO texture");

        // 绑定 FBO
        GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mFboId);
        checkGlError("glBindFramebuffer");

        // 将纹理绑定到 FBO 的颜色附件
        GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_COLOR_ATTACHMENT0,
                GLES30.GL_TEXTURE_2D, mFboTextureId, 0);
        checkGlError("glFramebufferTexture2D");

        // 检查 FBO 完整性
        int status = GLES30.glCheckFramebufferStatus(GLES30.GL_FRAMEBUFFER);
        if (status != GLES30.GL_FRAMEBUFFER_COMPLETE) {
            throw new RuntimeException("Framebuffer is not complete: " + status);
        }

        // 解绑 FBO
        GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0);
    }

    private int width;
    private int height;

    @Override
    public void onSurfaceChanged(GL10 unused, int width, int height) {
        GLES30.glViewport(0, 0, width, height);
        this.width = width;
        this.height = height;

        // 初始化 FBO
        // 这里假设 onSurfaceChanged 已经在初始化时被调用，且 width 和 height 已被赋值
        // 如果 onSurfaceChanged 尚未被调用，请考虑在这里调用 initFBO(width, height) 或在变量定义时初始化默认尺寸
        initFBO(width, height);
    }

    @Override
    public void onDrawFrame(GL10 unused) {
        // 1. 渲染到 FBO
        GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mFboId);
        GLES30.glViewport(0, 0, width, height);
        GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);

        // 更新纹理图像
        SurfaceTexture surfaceTexture = mGLSurfaceView.getSurfaceTexture();
        if (surfaceTexture == null) {
            GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0);
            return;
        }

        //synchronized (this) {
        surfaceTexture.updateTexImage();
        surfaceTexture.getTransformMatrix(mTransformMatrix);
        //}

        GLES30.glUseProgram(mProgram);
        checkGlError("glUseProgram (FBO)");

        // 设置变换矩阵
        int uMatrixLocation = GLES30.glGetUniformLocation(mProgram, "uTextureMatrix");
        GLES30.glUniformMatrix4fv(uMatrixLocation, 1, false, mTransformMatrix, 0);

        // 绑定外部纹理
        GLES30.glActiveTexture(GLES30.GL_TEXTURE0);
        GLES30.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mOESTextureId);

        // 设置纹理采样器
        int uTextureLocation = GLES30.glGetUniformLocation(mProgram, "uTexture");
        GLES30.glUniform1i(uTextureLocation, 0);


        // 设置顶点属性
        GLES30.glVertexAttribPointer(0, 3, GLES30.GL_FLOAT, false, 0, mVertexBuffer);
        GLES30.glEnableVertexAttribArray(0);
        GLES30.glVertexAttribPointer(1, 2, GLES30.GL_FLOAT, false, 0, mTexCoordBuffer);
        GLES30.glEnableVertexAttribArray(1);

        // 绘制到 FBO
        GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, 4);

        // 清理
        GLES30.glDisableVertexAttribArray(0);
        GLES30.glDisableVertexAttribArray(1);
        checkGlError("onDrawFrame (FBO)");

        // 2. 渲染 FBO 的纹理到屏幕
        GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0);
        GLES30.glViewport(0, 0, width, height);
        GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);

        GLES30.glUseProgram(mProgram2D);
        checkGlError("glUseProgram2D");

        // 绑定 FBO 纹理作为 2D 纹理
        GLES30.glActiveTexture(GLES30.GL_TEXTURE0);
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mFboTextureId);

        // 设置纹理采样器
        int uScreenTextureLocation = GLES30.glGetUniformLocation(mProgram2D, "uScreenTexture");
        GLES30.glUniform1i(uScreenTextureLocation, 0);


        // 设置其他 uniforms
        int mViewportInfoUniform = GLES30.glGetUniformLocation(mProgram2D, "uViewportInfo");
        int mSuperResolutionUniform = GLES30.glGetUniformLocation(mProgram2D, "uSuperResolution");

        GLES30.glUniform1i(mSuperResolutionUniform, superResolutionUniform);
        GLES30.glUniform4f(mViewportInfoUniform, 1f / width, 1f / height, width, height);

        Log.i(this.getClass().getSimpleName(), "===============> mSuperResolutionUniform = " + superResolutionUniform);

        // 设置顶点属性
        GLES30.glVertexAttribPointer(0, 3, GLES30.GL_FLOAT, false, 0, mVertexBuffer);
        GLES30.glEnableVertexAttribArray(0);
        GLES30.glVertexAttribPointer(1, 2, GLES30.GL_FLOAT, false, 0, mTexCoordBuffer);
        GLES30.glEnableVertexAttribArray(1);

        // 绘制到屏幕
        GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, 4);


        // 清理
        GLES30.glDisableVertexAttribArray(0);
        GLES30.glDisableVertexAttribArray(1);
        checkGlError("onDrawFrame (Screen)");
    }

    @Override
    public void onFrameAvailable(SurfaceTexture surfaceTexture) {
        mGLSurfaceView.requestRender();
    }

    /**
     * 创建着色器程序
     *
     * @param vertexSource   顶点着色器源码
     * @param fragmentSource 片段着色器源码
     * @return 程序对象 ID
     */
    private int createProgram(String vertexSource, String fragmentSource) {
        int vertexShader = loadShader(GLES30.GL_VERTEX_SHADER, vertexSource);
        if (vertexShader == 0) {
            Log.e("CameraRenderer", "Vertex shader compilation failed.");
            return 0;
        }

        int fragmentShader = loadShader(GLES30.GL_FRAGMENT_SHADER, fragmentSource);
        if (fragmentShader == 0) {
            Log.e("CameraRenderer", "Fragment shader compilation failed.");
            return 0;
        }

        int program = GLES30.glCreateProgram();
        if (program == 0) {
            Log.e("CameraRenderer", "Could not create program.");
            return 0;
        }

        GLES30.glAttachShader(program, vertexShader);
        GLES30.glAttachShader(program, fragmentShader);
        GLES30.glLinkProgram(program);

        // 检查链接状态
        int[] linkStatus = new int[1];
        GLES30.glGetProgramiv(program, GLES30.GL_LINK_STATUS, linkStatus, 0);
        if (linkStatus[0] != GLES30.GL_TRUE) {
            String log = GLES30.glGetProgramInfoLog(program);
            Log.e("CameraRenderer", "Program linking failed: " + log);
            GLES30.glDeleteProgram(program);
            return 0;
        }


        // 删除着色器，已链接到程序中
        GLES30.glDeleteShader(vertexShader);
        GLES30.glDeleteShader(fragmentShader);

        return program;
    }

    /**
     * 加载并编译着色器
     *
     * @param type       着色器类型
     * @param shaderCode 着色器源码
     * @return 着色器对象 ID
     */
    private int loadShader(int type, String shaderCode) {
        int shader = GLES30.glCreateShader(type);
        if (shader == 0) {
            Log.e("CameraRenderer", "Could not create shader of type " + type);
            return 0;
        }

        GLES30.glShaderSource(shader, shaderCode);
        GLES30.glCompileShader(shader);

        // 检查编译状态
        int[] compileStatus = new int[1];
        GLES30.glGetShaderiv(shader, GLES30.GL_COMPILE_STATUS, compileStatus, 0);
        if (compileStatus[0] == 0) {
            String log = GLES30.glGetShaderInfoLog(shader);
            Log.e("CameraRenderer", "Shader compilation failed: " + log);
            GLES30.glDeleteShader(shader);
            return 0;
        }

        return shader;
    }

    /**
     * 错误检查方法
     *
     * @param op 操作名称
     */
    private void checkGlError(String op) {
        int error;
        while ((error = GLES30.glGetError()) != GLES30.GL_NO_ERROR) {
            Log.e("CameraRenderer", op + ": glError " + error);
            throw new RuntimeException(op + ": glError " + error);
        }
    }
}

